package com.canyou.utils;

import cn.hutool.core.date.DateUtil;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

/**
 * 新版阿里大于发送短信服务工具接口
 *
 * @author fy
 * @date 2019-05-15 9:33
 */
@Component
@Slf4j
@Setter
@ConfigurationProperties("sms.dy")
public class DySmsUtils {

    /**
     * 此处需要替换成开发者自己的AK(在阿里云访问控制台寻找)
     */
    private String accessKeyId;
    private String accessKeySecret;

    /**
     * 短信API产品域名,开发者无需替换
     */
    private String domain;

    /**
     * 短信签名-可在短信控制台中找到
     */
    private String signName;

    /**
     * regionId和endpointName, 暂不支持region化
     */
    private String regionId;

    private String endpointName;

    private static final String PRODUCT = "Dysmsapi";
    private static final String OK = "OK";
    private static final String QUERY_DATE_FORMAT = "yyyyMMdd";

    /**
     * 发送短信基方法, 封装异常返回自己的异常, 记录日志信息
     *
     * @param smsTemplateCode 短信模板code
     * @param phoneNumber     接收短信者手机号
     * @param para            模板参数, 没有可传null
     * @return
     */
    public SendSmsResponse send(String smsTemplateCode, String phoneNumber, Map<String, String> para) throws ClientException {
        String paraStr = null;
        if (para != null && para.size() > 0) {
            paraStr = JsonUtils.toString(para);
        }
        return send(smsTemplateCode, phoneNumber, paraStr);
    }

    /**
     * 发送短信基方法, 封装异常返回自己的异常, 记录日志信息
     *
     * @param smsTemplateCode 短信模板code
     * @param phoneNumber     接收短信者手机号
     * @param paraStr         模板参数, 没有可传null
     * @return
     */
    public SendSmsResponse send(String smsTemplateCode, String phoneNumber, String paraStr) throws ClientException {
        if (StringUtils.isBlank(smsTemplateCode) || StringUtils.isBlank(phoneNumber)) {
            throw new IllegalArgumentException("短信模板code或送达手机号不能为空");
        }
        try {
            IAcsClient acsClient = getClient();
            SendSmsRequest request = new SendSmsRequest();
            request.setSysMethod(MethodType.POST);
            // 必填:待发送手机号。支持以逗号分隔的形式进行批量调用，批量上限为1000个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式；发送国际/港澳台消息时，接收号码格式为国际区号+号码，如“85200000000”
            request.setPhoneNumbers(phoneNumber);
            // 必填:短信签名-可在短信控制台中找到
            request.setSignName(signName);
            // 必填:短信模板-可在短信控制台中找到，发送国际/港澳台消息时，请使用国际/港澳台短信模版
            request.setTemplateCode(smsTemplateCode);
            // 可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
            // 友情提示:如果JSON中需要带换行符,请参照标准的JSON协议对换行符的要求,比如短信内容中包含\r\n的情况在JSON中需要表示成\\r\\n,否则会导致JSON在服务端解析失败
            if (StringUtils.isNotBlank(paraStr)) {
                request.setTemplateParam(paraStr);
            }
            // 请求失败这里会抛ClientException异常
            SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
            // 记录发送信息
            log.info("发送短信:smsTemplateCode=" + smsTemplateCode +
                    ";phoneNumber=" + phoneNumber +
                    ";para=" + paraStr +
                    ";Code=" + sendSmsResponse.getCode() +
                    ";Message=" + sendSmsResponse.getMessage() +
                    ";RequestId=" + sendSmsResponse.getRequestId() +
                    ";BizId=" + sendSmsResponse.getBizId());
            return sendSmsResponse;
        } catch (ClientException e) {
            // 记录错误
            log.error("发送短信出错:smsTemplateCode=" + smsTemplateCode +
                    ";phoneNumber=" + phoneNumber +
                    ";para=" + paraStr +
                    ";RequestId=" + e.getRequestId() +
                    ";ErrCode=" + e.getErrCode() +
                    ";ErrMsg=" + e.getErrMsg() +
                    ";ErrType=" + e.getErrorType().name() +
                    ExceptionUtils.getStackTrace(e), e);
            throw e;
        }
    }

    /**
     * 以发生异常时进行3次重试的方式发送短信, 如果最后一次还是发生异常, 会抛出最后一次异常
     *
     * @param smsTemplateCode 短信模板code
     * @param phoneNumber     接收短信者手机号
     * @param para            模板参数, 没有可传null
     * @return
     */
    public SendSmsResponse sendOnRetry(String smsTemplateCode, String phoneNumber, Map<String, String> para) {
        return RetryUtils.retryOnException(() -> send(smsTemplateCode, phoneNumber, para));
    }

    /**
     * 查询短信发送状态详情
     *
     * @param phoneNumber 接收短信者手机号
     * @param bizId       流水账id(可选)
     * @param sendDate    发送日期
     * @param currentPage 当前页
     * @param pageSize    每页条数
     * @return
     * @throws ClientException
     */
    public QuerySendDetailsResponse querySendDetails(String phoneNumber,
                                                     String bizId,
                                                     String sendDate,
                                                     long currentPage,
                                                     long pageSize) throws ClientException {
        if (StringUtils.isBlank(phoneNumber) || StringUtils.isBlank(bizId)) {
            throw new IllegalArgumentException("送达手机号或bizId不能为空");
        }

        IAcsClient acsClient = getClient();

        QuerySendDetailsRequest request = new QuerySendDetailsRequest();
        request.setPhoneNumber(phoneNumber);
        // 可选-调用发送短信接口时返回的BizId
        request.setBizId(bizId);
        // 必填-短信发送的日期 支持30天内记录查询（可查其中一天的发送数据），格式yyyyMMdd
        request.setSendDate(sendDate);
        request.setPageSize(pageSize);
        request.setCurrentPage(currentPage);

        QuerySendDetailsResponse querySendDetailsResponse = acsClient.getAcsResponse(request);

        return querySendDetailsResponse;
    }

    /**
     * 获取单条短信发送状态
     *
     * @param phoneNumber 手机号
     * @param bizId       流水账id
     * @param sendDate    发送日期
     * @return
     * @throws ClientException
     */
    public QuerySendDetailsResponse.SmsSendDetailDTO querySendDetail(String phoneNumber,
                                                                     String bizId,
                                                                     LocalDateTime sendDate) throws ClientException {
        QuerySendDetailsResponse detailsResponse = querySendDetails(phoneNumber, bizId,
                DateUtil.format(DateUtils.localDateTime2Date(sendDate), QUERY_DATE_FORMAT), 1, 1);
        if (detailsResponse.getCode() != null && OK.equals(detailsResponse.getCode())) {
            List<QuerySendDetailsResponse.SmsSendDetailDTO> detailList = detailsResponse.getSmsSendDetailDTOs();
            if (detailList != null && !detailList.isEmpty()) {
                return detailList.get(0);
            }
        }
        return null;
    }

    private IAcsClient getClient() throws ClientException {
        // 初始化ascClient,暂时不支持多region（请勿修改）
        IClientProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
        ///DefaultProfile.addEndpoint(endpointName, regionId, PRODUCT, domain);
        DefaultProfile.addEndpoint(regionId, PRODUCT, domain);
        IAcsClient acsClient = new DefaultAcsClient(profile);
        return acsClient;
    }

    public boolean checkSendSuccess(SendSmsResponse sendSmsResponse) {
        if (sendSmsResponse != null && sendSmsResponse.getCode() != null && OK.equals(sendSmsResponse.getCode())) {
            // 请求成功
            return true;
        }
        return false;
    }

}
